import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';

import '../../config/AppInfo.dart';

class DownFile {
  static Dio dio = Dio();
  static create() {
    dio.options.connectTimeout = Duration(milliseconds: RequestTimeout); //秒
    dio.options.receiveTimeout = Duration(milliseconds: RequestTimeout);
  }

  /**
      获取手机的存储目录路径
      // getExternalStorageDirectory() 获取的是  android 的外部存储 （External Storage）
      getApplicationDocumentsDirectory 获取的是 ios 的Documents` or `Downloads` 目录
   */
  static Future<String> getPhoneLocalPath() async {
    if (Platform.isIOS) {
      final directory = await getApplicationDocumentsDirectory();
      return directory.path;
    } else {
      final directory = await getApplicationDocumentsDirectory();
      return directory.path;
      // final directory = await getExternalStorageDirectory();
      // return directory!.path;
    }
  }

  /**
   * apk目录
   */
  static Future<String> getPhoneLocalPathCps() async {
    if (Platform.isIOS) {
      final directory = await getApplicationDocumentsDirectory();
      return directory.path;
    } else {
      final directory = await getExternalStorageDirectory();
      return directory!.path;
    }
  }

  /**
   * 从Asset获取文件
   */
  static Future<File> fromAsset(String asset, String filename) async {
    // To open from assets, you can copy them to the app storage folder, and the access them "locally"
    Completer<File> completer = Completer();

    try {
      var dir = await getApplicationDocumentsDirectory();
      File file = File("${dir.path}/$filename");
      var data = await rootBundle.load(asset);
      var bytes = data.buffer.asUint8List();
      await file.writeAsBytes(bytes, flush: true);
      completer.complete(file);
    } catch (e) {
      throw Exception('Error parsing asset file!');
    }

    return completer.future;
  }

  //android 模拟器需要解压在这个目录
  static Future<String> getAndroidCoreLocalPath() async {
    String savePath = (await getApplicationSupportDirectory()).path;
    savePath = savePath.substring(0, savePath.lastIndexOf('/') + 1) + 'cores';
    return savePath;
  }

  /**
   * 判断本地文件是否存在
   */
  static Future<bool> checkExistsFile(String savePath) async {
    var file = File(savePath);
    var exists = await file.exists();
    return exists;
  }

  /**
   * 创建文件，存在则不创建
   */
  static Future<void> createFileIfNotExists(String path) async {
    bool exists = await checkExistsFile(path);
    if (exists == false) {
      var file = File(path);
      await file.create(recursive: true);
    }
  }

  /**
   * 创建目录，存在则不创建
   */
  static Future<void> createDirectoryIfNotExists(String dir) async {
    Directory directory = Directory(dir);
    if (await directory.exists() == false) {
      directory.createSync(recursive: true);
    }
  }

  /**
   * 断点下载文件
   */
  static Future<void> download({
    required String url,
    required String savePath,
    ProgressCallback? onReceiveProgress,
    Function()? done,
    Function(Exception)? failed,
    required CancelToken cancelToken,
    Function()? onCancel,
  }) async {
    int downloadStart = 0;
    File f = File(savePath);
    if (await f.exists() == true) {
      downloadStart = f.lengthSync();
    } else {
      await f.create(recursive: true);
      downloadStart = 0;
    }
    print("开始位置：$downloadStart");
    var dio = Dio();
    try {
      var response = await dio.get<ResponseBody>(
        url,
        options: Options(
          responseType: ResponseType.stream,
          followRedirects: true,
          validateStatus: (status) {
            if (status == null) return false;
            return status < 500;
          },
          headers: {
            "range": "bytes=$downloadStart-",
          },
        ),
      );
      File file = File(savePath);
      RandomAccessFile raf = file.openSync(mode: FileMode.append);
      int received = downloadStart;
      int total = await getContentLength(response);
      Stream<Uint8List> stream = response.data!.stream;
      StreamSubscription<Uint8List>? subscription;
      subscription = stream.listen(
        (data) async {
          raf.writeFromSync(data); //同步写入
          received += data.length;
          onReceiveProgress?.call(received, total);
        },
        onDone: () async {
          await raf.close();
          done?.call();
        },
        onError: (e) async {
          await raf.close();
          failed?.call(e);
        },
        cancelOnError: true,
      );
      cancelToken.whenCancel.then((_) async {
        try {
          await subscription?.cancel();
          await raf.close();
          if (onCancel != null) {
            onCancel();
          }
        } catch (e) {
          print("${e}");
        }
      });
    } on DioError catch (error) {
      // MyToast.show("网络不给力，请稍后重试");
      print("下载出错 error ${error}");
      if (CancelToken.isCancel(error)) {
        print("下载取消");
      } else {
        failed?.call(error);
      }
    }
  }

  /**
   * 获取下载的文件大小
   */
  static Future<int> getContentLength(Response<ResponseBody> response) async {
    try {
      var headerContent =
          response.headers.value(HttpHeaders.contentRangeHeader);
      //print("下载文件headerContent:$headerContent");
      if (headerContent != null) {
        return int.parse(headerContent.split('/').last);
      } else {
        return 0;
      }
    } catch (e) {
      print("_getContentLength error = $e");
      return 0;
    }
  }

  /**
   * 根据url 获取下载的文件大小
   */
  static Future<Map<String, int>> getUrlContentLength(
      String url, String type) async {
    int downloadStart = 0;
    var response = await dio.get<ResponseBody>(
      url,
      options: Options(
        responseType: ResponseType.stream,
        followRedirects: true,
        validateStatus: (status) {
          print("validateStatus(${status})");
          if (status == null) return false;
          return status == 206;
        },
        headers: {
          "range": "bytes=$downloadStart-",
        },
      ),
    );
    print("状态码是${response.data!.statusCode}");

    try {
      int total = 0;
      if (response.data!.statusCode == 206) {
        total = await getContentLength(response);
      }

      return Future(() {
        Map<String, int> map = {};
        map[type] = total;
        return map;
      });
    } catch (e) {
      print("获取大小失败 url(${url})");
    }

    return Future(() {
      Map<String, int> map = {};
      map[type] = 0;
      return map;
    });
  }
}
